/*
 * SerialProtocol.cpp
 *
 *  Created on: Apr 20, 2012
 *      Author: steffen
 */

#include "SerialProtocol.h"

#include <Arduino.h>

SerialProtocol
	::SerialProtocol(Stream& myStream) :
	 	 _mStream(myStream),
	 	 _mState(IDLE),
	 	 _mRcvTimoutMs(10)
{
}

void SerialProtocol
	::Initialize(void)
{
}

CRS232ProtocolPacket* SerialProtocol
	::Service(void)
{
	CRS232ProtocolPacket* _retVal = 0x0;

	static char _receivedBytes[NUMBER_OF_COMMAND_BYTES + NUMBER_OF_DATA_BYTES];
	static unsigned int _receivedBytesIndex = 0;

	switch (_mState)
	{
		case IDLE:
			{
				_receivedBytesIndex = 0;

				if (_mStream.available())
				{
					CounterReset();

					// Start byte received?
					if (_mStream.read() == START_BYTE)
					{
						_mState = RECEIVE;
					}
				}
			}
			break;
		case RECEIVE:
			{
				if (_mStream.available())
				{
					while (_mStream.available())
					{
						_receivedBytes[_receivedBytesIndex++] = _mStream.read();

						CounterReset();

						if (_receivedBytesIndex == (NUMBER_OF_COMMAND_BYTES + NUMBER_OF_DATA_BYTES))
						{
							_mState = VERIFY;
							break;
						}
					}
				}
				else if(CounterExpired(_mRcvTimoutMs))
				{
					_mState = IDLE;
				}
			}
			break;
		case VERIFY:
			{
				if (_mStream.available())
				{
					char _checkSum = _mStream.read();

					if (VerifyReceivedPacket(_receivedBytes, NUMBER_OF_COMMAND_BYTES + NUMBER_OF_DATA_BYTES, _checkSum))
					{
						_mPacketReceived.setPacket(_receivedBytes);

						_retVal = &_mPacketReceived;
					}

					_mState = IDLE;
				}
				else if(CounterExpired(_mRcvTimoutMs))
				{
					_mState = IDLE;
				}
			}
			break;
	}

	return _retVal;
}

void SerialProtocol
	::SendPacket(CRS232ProtocolPacket* ptrPacketToBeSent)
{
	// Send start byte
	SendCharacter(START_BYTE);

	char ptrPacket[NUMBER_OF_COMMAND_BYTES + NUMBER_OF_DATA_BYTES];

	ptrPacketToBeSent->getPacket(ptrPacket);

	char cChecksum = 0;

	for(int i = 0; i < NUMBER_OF_COMMAND_BYTES + NUMBER_OF_DATA_BYTES; i++)
	{
		cChecksum ^= ptrPacket[i];
		SendCharacter(ptrPacket[i]);
	}

	// Send check byte
	SendCharacter(cChecksum);

}

bool SerialProtocol
    ::CounterExpired(unsigned long expirationDelayMs)
{
    bool _expired = false;

    unsigned long _counterMsNew = millis();

    // Counter overflow?
    if (_counterMsNew < _mCounterMsLast)
    {
        // Delay expired?
        if (((0xFFFFFFFF - _mCounterMsLast) + _counterMsNew) > expirationDelayMs)
        {
            _expired = true;
        }
    }
    else
    {
        // Delay expired?
        if ((_counterMsNew - _mCounterMsLast) > expirationDelayMs)
        {
            _expired = true;
        }
    }

    if (_expired)
    {
        // CounterReset(_counterMsNew);
        CounterReset();
    }

    return _expired;
}

void SerialProtocol
    ::CounterReset(unsigned long resetValue)
{
    if (resetValue)
    {
        _mCounterMsLast = resetValue;
    }
    else
    {
        _mCounterMsLast = millis();
    }
}

bool SerialProtocol
	::VerifyReceivedPacket(char* ptrRxChars, int iNumberOfRxChars, char cChecksum)
{
	char cChecksumCalulated;

	if( iNumberOfRxChars < 1 )
		return false;


	cChecksumCalulated = ptrRxChars[0];

	for(int i = 1; i < iNumberOfRxChars; i++)
		cChecksumCalulated ^= ptrRxChars[i];

	// return cChecksumCalulated == cChecksum ? 1 : 0;
	// TODO: Enable checksum verification
	return true;
}

void SerialProtocol
	::SendCharacter(char cCharToBeSent)
{
	_mStream.print(cCharToBeSent);
}
